package com.lxk.service.impl;

import com.lxk.common.Constant;
import com.lxk.common.EventTypeEnum;
import com.lxk.common.GsonUtils;
import com.lxk.dao.*;
import com.lxk.dao.es.BlogEsDao;
import com.lxk.domain.*;
import com.lxk.domain.dto.BlogInfoDto;
import com.lxk.domain.dto.PageInfo;
import com.lxk.domain.es.BlogEsEntity;
import com.lxk.domain.vo.IndexBlogVo;
import com.lxk.service.BlogService;
import com.lxk.service.PageHelper;
import com.lxk.service.event.EventBusEnum;
import com.lxk.service.event.EventBusManager;
import com.lxk.service.event.domain.EventDto;
import com.lxk.service.utils.SnowflakeIdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;

import static com.lxk.common.Constant.BConstants.DEFAULT_TYPE_ID;
import static com.lxk.common.Constant.BConstants.NUM_USER_ID;

/**
 * Created by Lxk on 2020/6/16.
 */
@Service
public class BlogServiceImpl implements BlogService {

    private static final Logger logger = LoggerFactory.getLogger(BlogServiceImpl.class);

    @Autowired
    private BlogDbDao blogDbDao;

    @Autowired
    private BlogEsDao blogEsDao;

    @Autowired
    private BlogTagRelDao blogTagRelDao;

    @Autowired
    private TypeDbDao typeDbDao;

    @Autowired
    private UserDbDao userDbDao;

    @Autowired
    private TagDbDao tagDbDao;

    @Autowired
    private BlogBackupDao blogBackupDao;

    @Autowired
    private EventBusManager eventBusManager;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Override
    public void buildPageInfo(PageInfo pageInfo) {
        PageHelper.queryByPageInfo(pageInfo, blogDbDao);
    }

    /**
     * 博客编辑后保存，会异步同步Es，可能存在短时间不一致，但是数据库数据强一致
     * @param blogInfoDto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveBlog(BlogInfoDto blogInfoDto) {
        //构造blogDb列表对象，只用于列表展示
        Long blogId = snowflakeIdWorker.nextId();
        Long backupId = snowflakeIdWorker.nextId();
        BlogDbEntity blogDbEntity = buildBlogDbEntity(blogInfoDto);
        blogDbEntity.setId(blogId);
        blogDbEntity.setBackupId(backupId);
        blogDbDao.insert(blogDbEntity);
        //构造blogDb详情对象，包含文档正文
        BlogBackupEntity blogBackupEntity = buildBlogBackupEntity(blogInfoDto);
        blogBackupEntity.setBlogId(blogId);
        blogBackupEntity.setId(backupId);
        blogBackupDao.insert(blogBackupEntity);
        //更新写入tag
        if (blogInfoDto.getTagIds() != null) {
            for (String tagId : blogInfoDto.getTagIds().split(",")) {
                BlogTagRelEntity blogTagRelEntity = new BlogTagRelEntity();
                blogTagRelEntity.setEsBlogId(blogDbEntity.getId());
                blogTagRelEntity.setDbBlogId(blogDbEntity.getId());
                blogTagRelEntity.setDbTagId(Long.parseLong(tagId));
                blogTagRelEntity.setTagName(tagDbDao.queryById(tagId).getName());
                blogTagRelDao.insert(blogTagRelEntity);
            }
        }
        EventDto eventDto = new EventDto();
        //构造Es同步信息并发布事件异步同步
        BlogEsEntity blogEsEntity = buildEsEntity(blogDbEntity,blogBackupEntity);
        eventDto.setEventType(EventTypeEnum.EVENT_BLOG_PUBLISH);
        eventDto.setContent(GsonUtils.getGson().toJson(blogEsEntity));
        eventBusManager.get(EventBusEnum.EVENT_BUS_PUBLISH).post(eventDto);

    }

    @Override
    public IndexBlogVo queryById(String id) {
        BlogDbEntity blogDbEntity = blogDbDao.queryById(Long.parseLong(id));
        UserDbEntity userDbEntity = userDbDao.queryById(blogDbEntity.getUserId());
        Optional<BlogEsEntity> blogEsEntityOptional = null;
        try {
            blogEsEntityOptional = blogEsDao.findById(blogDbEntity.getEsId());
        } catch (Exception e) {
            logger.error("id:{},访问es获取博客信息失败", id, e);
            BlogBackupEntity blogBackupEntity = blogBackupDao.queryByBlogId(Long.parseLong(id));
            BlogEsEntity blogEsEntity = new BlogEsEntity();
            blogEsEntity.setId(blogBackupEntity.getBlogId());
            blogEsEntity.setTitle(blogBackupEntity.getTitle());
            blogEsEntity.setContent(blogBackupEntity.getContent());
            blogEsEntityOptional = Optional.of(blogEsEntity);
        }
        List<TagDbEntity> tagDbEntityList = blogTagRelDao.queryTagsByBlogId(id);
        TypeDbEntity typeDbEntity = typeDbDao.queryById(String.valueOf(blogDbEntity.getTypeId()));
        IndexBlogVo indexBlogVo = new IndexBlogVo();
        BeanUtils.copyProperties(blogDbEntity, indexBlogVo);
        indexBlogVo.setUsername(userDbEntity.getUsername());
        indexBlogVo.setAvatar(userDbEntity.getAvatar());
        if (blogEsEntityOptional.isPresent()) {
            indexBlogVo.setTitle(blogEsEntityOptional.get().getTitle());
            indexBlogVo.setContent(blogEsEntityOptional.get().getContent());
        }
        indexBlogVo.setTags(tagDbEntityList);
        indexBlogVo.setTypeName(typeDbEntity.getName());
        indexBlogVo.setFirstPicInfo(blogDbEntity.getFirstPicInfo());
        indexBlogVo.setDesc(blogDbEntity.getDesc());
        return indexBlogVo;
    }

    @Override
    public void update(BlogDbEntity blogDbEntity) {
        blogDbDao.update(blogDbEntity);
    }

    /**
     * 删除博客和博客备份，同步事务更新备份表，
     * @param blogId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBlog(Long blogId) {
        if(ObjectUtils.isEmpty(blogId)){
            logger.error("待删除博客Id为空 :: blogId:{}",blogId);
            throw new RuntimeException("blog id is null!!");
        }
        BlogDbEntity blogFromDb = blogDbDao.queryById(blogId);
        blogFromDb.setYn(Constant.BConstants.YN_INVALID);
        blogDbDao.update(blogFromDb);
        BlogBackupEntity blogBackupFromDb = blogBackupDao.queryById(blogFromDb.getBackupId());
        blogBackupFromDb.setYn(Constant.BConstants.YN_INVALID);
        blogBackupDao.update(blogBackupFromDb);
        EventDto eventDto = new EventDto();
        BlogEsEntity blogEsEntity = new BlogEsEntity();
        blogEsEntity.setId(blogId);
        blogEsEntity.setBlogId(blogId);
        eventDto.setEventType(EventTypeEnum.EVENT_BLOG_PUBLISH);
        eventDto.setContent(GsonUtils.getGson().toJson(blogEsEntity));
        eventBusManager.get(EventBusEnum.EVENT_BUS_PUBLISH).post(eventDto);
    }

    @Override
    public List<BlogDbEntity> listRecommendTopN(Integer num) {
        return blogDbDao.listRecommendTopN(num);
    }

    @Override
    public List<BlogDbEntity> listRecentN(Integer num) {
        return blogDbDao.listRecentN(num);
    }

    /**
     * 构造内容备份对象
     * @param blogInfoDto
     * @return
     */
    private BlogBackupEntity buildBlogBackupEntity(BlogInfoDto blogInfoDto){
        BlogBackupEntity blogBackupEntity = new BlogBackupEntity();
        blogBackupEntity.setTitle(blogInfoDto.getTitle());
        blogBackupEntity.setContent(blogInfoDto.getContent());
        return blogBackupEntity;
    }

    /**
     * 构造博客列表对象
     * @param blogInfoDto
     * @return
     */
    private BlogDbEntity buildBlogDbEntity(BlogInfoDto blogInfoDto){
        BlogDbEntity blogDbEntity = new BlogDbEntity();
        blogDbEntity.setEsId(0L);
        blogDbEntity.setTitle(blogInfoDto.getTitle());
        blogDbEntity.setDesc(blogInfoDto.getDesc());
        blogDbEntity.setUserId(NUM_USER_ID);
        blogDbEntity.setTypeId(!StringUtils.isEmpty(blogInfoDto.getTypeId()) ? Long.parseLong(blogInfoDto.getTypeId()) : DEFAULT_TYPE_ID);
        if (blogInfoDto.getTypeId() != null) {
            TypeDbEntity typeDbEntity = typeDbDao.queryById(blogInfoDto.getTypeId());
            blogDbEntity.setTypeName(typeDbEntity.getName());
        }
        blogDbEntity.setBlogType(blogInfoDto.getBlogType());
        blogDbEntity.setAppreciation(blogInfoDto.getAppreciation() ? 1 : 0);
        blogDbEntity.setComented(blogInfoDto.getCommend() ? 1 : 0);
        blogDbEntity.setFirstPicInfo(blogInfoDto.getIndexPicture());
        blogDbEntity.setPublished(0);
        blogDbEntity.setRecommend(blogInfoDto.getRecommend() ? 1 : 0);
        blogDbEntity.setPublished(blogInfoDto.getPublished() ? 1 : 0);
        blogDbEntity.setShareStatement(blogInfoDto.getShareInfo() ? 1 : 0);
        blogDbEntity.setViewCount(0);
        blogDbEntity.setYn(1);
        return blogDbEntity;
    }

    /**
     * 构造博客同步Es对象
     * @param blogDbEntity
     * @param blogBackupEntity
     * @return
     */
    BlogEsEntity buildEsEntity(BlogDbEntity blogDbEntity,BlogBackupEntity blogBackupEntity){
        BlogEsEntity blogEsEntity = new BlogEsEntity();
        blogEsEntity.setId(blogDbEntity.getId());
        blogEsEntity.setBlogId(blogDbEntity.getId());
        blogEsEntity.setBlogBackupId(blogBackupEntity.getId());
        blogEsEntity.setUserId(blogDbEntity.getUserId());
        blogEsEntity.setTitle(blogDbEntity.getTitle());
        blogEsEntity.setDesc(blogDbEntity.getDesc());
        blogEsEntity.setContent(blogBackupEntity.getContent());
        boolean show = blogDbEntity.getPublished()==1&&blogDbEntity.getYn()== Constant.BConstants.YN_VALID ?true:false;
        blogEsEntity.setShow(show);
        return blogEsEntity;
    }

}
